//
//  DataLoader.swift
//  Risac
//
//  Created by remy on 2018/4/17.
//

import Moya

/// 数据加载管理
public protocol DataLoadable: AnyObject {
    /// 数据加载设置
    func dataLoadPrepare(loader: DataLoader)
    /// 数据加载结果
    func dataLoadResult(loader: DataLoader, result: NetworkManager.ResultType)
}
public extension DataLoadable {
    func dataLoadResult(loader: DataLoader, result: NetworkManager.ResultType) {}
}

/// 扩展网络管理
public final class DataLoader {
    public enum State {
        /// 无请求
        case idle
        /// 请求取消
        case cancel
        /// 正在请求
        case loading
        /// 请求错误
        case error
        /// 空数据,视具体业务而定
        case empty
        /// 无网络状态
        case noNetwork
        /// 请求成功
        case success
        
        public func isAnyOf(_ values: [State]) -> Bool {
            return values.contains(where: { $0 == self })
        }
    }
    
    /// 请求对象
    public private(set) var request: Moya.Cancellable?
    /// 分页信息类型
    public typealias PageInfo = (index: Int, size: Int)
    /// 默认起始分页信息
    public static var defaultStartPageInfo: PageInfo = (1, 20)
    /// 起始分页信息
    public var startPageInfo: PageInfo = defaultStartPageInfo
    /// 分页信息
    public lazy var pageInfo: PageInfo = startPageInfo
    /// Moya.TargetType
    public var target: Moya.TargetType?
    /// 请求配置
    public var options: ((inout NetworkManager.Options) -> Void)?
    /// 代理对象
    public private(set) weak var loadable: DataLoadable?
    /// 请求状态
    public var state: State = .idle
    /// 是否分页请求
    public private(set) var isPageLoad: Bool = false
    /// 是否首页
    public var isFirstPage: Bool {
        return isPageLoad && pageInfo.index == startPageInfo.index
    }
    /// 没有更多数据
    public var noMoreData: Bool = false
    private weak var pack: ListPack?
    
    public init(target: DataLoadable, closure: ((DataLoader) -> Void)? = nil) {
        self.loadable = target
        closure?(self)
    }
    
    private func _load() {
        self.loadable?.dataLoadPrepare(loader: self)
        guard let target = self.target else { return }
        self.state = .loading
        NetworkManager.request(target: target, options: options) {
            [weak self] in
            guard let self = self else { return }
            if case .success = $0 {
                self.state = .success
            } else if case let .failure(error) = $0 {
                if (error as NSError).code == 6 {
                    self.state = .noNetwork
                } else {
                    self.state = .error
                }
            }
            if let pack = self.pack {
                return self.packHandler(pack: pack, result: $0)
            }
            self.loadable?.dataLoadResult(loader: self, result: $0)
        }
    }
    
    private func packHandler(pack: ListPack, result: NetworkManager.ResultType) {
        guard self.state == .success else {
            self.loadable?.dataLoadResult(loader: self, result: result)
            return pack.endRefresh(reset: isFirstPage, last: false, increasedCount: 0)
        }
        let model = pack.getModel()
        if isFirstPage { model.removeAll() }
        let prevItemsCount = model.cellItemsCount(at: nil)
        self.loadable?.dataLoadResult(loader: self, result: result)
        let itemsCount = model.cellItemsCount(at: nil)
        if itemsCount > 0 {
            if itemsCount > prevItemsCount {
                pack.endRefresh(reset: isFirstPage, last: false, increasedCount: itemsCount - prevItemsCount)
                self.pageInfo.index += 1
            } else {
                pack.endRefresh(reset: isFirstPage, last: true, increasedCount: 0)
            }
        } else {
            self.state = .empty
            pack.endRefresh(reset: isFirstPage, last: true, increasedCount: 0)
        }
    }
    
    public func setPack(_ pack: ListPack) {
        self.pack = pack
    }
    
    public func clearPack() {
        self.setPackRefresh(.none)
        self.pack = nil
    }
    
    public func setPackRefresh(_ type: RefreshType) {
        guard type != .none else {
            self.pack?.setRefresh(type: .none, closure: nil)
            return
        }
        pack?.setRefresh(type: type) { [weak self] (action) in
            guard let self = self else { return }
            if action.isAnyOf([.pullDown, .pullUp]) {
                self.pageReset()
            } else {
                self.pageTurn()
            }
        }
    }
    
    public func load() {
        self.isPageLoad = false
        self._load()
    }
    
    public func pageReset() {
        self.pageTurn(self.startPageInfo.index)
    }
    
    public func pageTurn(_ index: Int? = nil) {
        self.isPageLoad = true
        if let index = index { self.pageInfo.index = index }
        self.cancel()
        self._load()
    }
    
    public func cancel() {
        self.request?.cancel()
        self.state = .cancel
    }
}
